package com.wanban.downloader.parse

import android.text.TextUtils
import android.util.Log
import com.blankj.utilcode.util.CloseUtils
import com.blankj.utilcode.util.FileUtils
import com.wanban.downloader.db.DownloadEntity
import com.wanban.downloader.http.HttpConnectUtils
import com.wanban.downloader.listener.OnVideoUrlParseListener
import com.wanban.downloader.model.DownloadParams
import com.wanban.downloader.model.M3U8
import com.wanban.downloader.model.M3U8Seg
import com.wanban.downloader.model.M3U8Stream
import com.wanban.downloader.utils.CacheM3U8Utils
import com.wanban.downloader.utils.CommonUtils
import com.wanban.downloader.utils.Constants
import com.wanban.downloader.utils.Constants.ERROR_CODE_MULTI_STREAMS_REQUEST
import com.wanban.downloader.utils.Constants.ERROR_CODE_REQUEST
import com.wanban.downloader.utils.ErrorMessage
import com.wanban.downloader.utils.FileDownloadPathUtils
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okhttp3.internal.closeQuietly
import java.io.BufferedReader
import java.io.DataOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.io.InputStreamReader
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException


/**
 * m3u8解析类
 */
class RxM3U8ParseUtilsV2(private val task: DownloadEntity) {

    // 是否中断任务执行
    @Volatile
    private var interrupted = false
    private var parseDisposable: Disposable? = null

    /**
     * 解析M3U, 将其转换成M3U8对象
     */
    fun parseVideo(callback: OnVideoUrlParseListener) {
//        if (task.m3u8 != null && task.m3u8?.tsList?.isNotEmpty() == true) { // 如果tsList有内容，可能已经写入内存中
//           if (task.m3u8?.isM3U8 == true) { // 如果已经创建本地和代理M3U, 说明已经解析完成
//               val proxyM3U8FileDirPath = task.fileDirPath + File.separator + FileDownloadPathUtils.PROXY_M3U_FILE_DIR_NAME
//               val isProxyM3UFileAvailable = isFileAvailable(proxyM3U8FileDirPath, FileDownloadPathUtils.PROXY_M3U_FILE_NAME)
//               if (isProxyM3UFileAvailable) {
//                   callback.onSuccess(task.m3u8)
//                   return
//               }
//               // 任何一项缓存文件失败，都要重新解析
//               FileUtils.delete(proxyM3U8FileDirPath)
//               task.m3u8?.tsList?.clear()
//               task.m3u8?.tsList = null
//               task.m3u8 = null
//           } else { // Mp4 直接返回解析成功
//               callback.onSuccess(task.m3u8)
//               return
//           }
//        }
//        // 重新解析
//        task.m3u8 = M3U8(task.url)
//        task.m3u8?.success = false
//        task.m3u8?.videoName = task.getFileName()
//        if (!CommonUtils.matchHttpSchema(task.url)) {
//            callback.onError(ErrorMessage.ERROR_SCHEMA_CANNOT_PARSE)
//            return
//        }
//        doParse(task.url, callback)

        parseVideoV2(callback)
    }


    private fun parseVideoV2(callback: OnVideoUrlParseListener){
        if (task.m3u8?.tsList?.isNotEmpty() == true) { // 如果m3u8内容已经在内存中，直接返回
            callback.onSuccess(task.m3u8)
            return
        }

        // 当前m3u8没有初始化，先判断是否有本地缓存文件
        val cacheM3u8 = CacheM3U8Utils.getM3U8FromCache(task.getFileName())
        if (cacheM3u8 != null) {
            task.m3u8 = cacheM3u8
            callback.onSuccess(task.m3u8)
            return
        }

        // 缓存文件也没有，只能重新解析
        task.m3u8 = M3U8(task.url)
        task.m3u8?.success = false
        task.m3u8?.videoName = task.getFileName()
        if (!CommonUtils.matchHttpSchema(task.url)) {
            callback.onError(ErrorMessage.ERROR_SCHEMA_CANNOT_PARSE)
            return
        }
        doParse(task.url, callback)
    }


    private fun doParse(url: String, callback: OnVideoUrlParseListener){
        val httpConnectUtils = HttpConnectUtils()
        parseDisposable?.dispose()
        parseDisposable = Observable.create<M3U8> {
            // 2.请求url
            val response = httpConnectUtils.open(url, task.header)
            if (response == null) {
                if (it.isDisposed) return@create
                it.onError(NullPointerException(ErrorMessage.ERROR_HTTPCONNECTION_ERROR))
                return@create
            }
            val finalUrl = response.request.url.toUrl().toString()
            if (TextUtils.isEmpty(finalUrl)) {
                response.closeQuietly()
                if (it.isDisposed) return@create
                it.onError(NullPointerException(ErrorMessage.ERROR_FINAL_URL_IS_NULL))
                return@create
            }
            task.m3u8?.finalUrl = finalUrl
            val contentType = response.body?.contentType()?.toString()?:""
            task.m3u8?.isM3U8 = task.m3u8?.finalUrl?.contains(Constants.M3U8, ignoreCase = true) == true || CommonUtils.isM3U8Mimetype(contentType)
            if (task.m3u8?.isM3U8 == true) { // 是m3u8
                val inputStream = response.body?.byteStream()
                if (inputStream == null) {
                    if (it.isDisposed) return@create
                    it.onError(NullPointerException(ErrorMessage.ERROR_PARSE_M3U8))
                    return@create
                }
                if (interrupted) {
                    if (it.isDisposed) return@create
                    it.onComplete()
                    return@create
                }
                // 3.解析
                parseM3U8(inputStream)
                // 如果存在多个轨道流，重新请求
                if (task.m3u8?.streams?.isNotEmpty() == true) {
                    if (it.isDisposed) return@create
                    task.m3u8?.errorCode = ERROR_CODE_MULTI_STREAMS_REQUEST
                    it.onNext(task.m3u8!!)
                    return@create
                }
                if (interrupted) {
                    if (it.isDisposed) return@create
                    it.onComplete()
                    return@create
                }

                // 4.创建缓存TsList文件
                val isCachedSuccessful = CacheM3U8Utils.cacheM3U8(task)

                // 5.缓存代理m3u，里面每个ts的播放的地址是本地地址 ProxyM3U8
                val isCreateProxyM3U8Success = createCacheM3U8Files()
                if (!isCreateProxyM3U8Success) {
                    if (it.isDisposed) return@create
                    it.onError(NullPointerException(task.m3u8?.errorMsg))
                    return@create
                }
            } else { // mp4
                val fileSize = response.body?.contentLength()?:0L
                if (fileSize <= 0) {
                    if (it.isDisposed) return@create
                    it.onError(NullPointerException(ErrorMessage.ERROR_MP4_FILE_LENGTH))
                    return@create
                }

                val size = fileSize / DownloadParams.maxThreadNumber
                for (i in 0 until DownloadParams.maxThreadNumber) {
                    // 1.确定每个线程的下载区间
                    // 2.开启对应的子线程
                    val startIndex = i * size //开始位置
                    var endIndex = (i + 1) * size - 1 //结束位置
                    // 最后一个线程
                    if (i == DownloadParams.maxThreadNumber - 1) {
                        endIndex = fileSize - 1
                    }
                    val m3u8Seg = M3U8Seg()
                    m3u8Seg.url = task.m3u8?.finalUrl
                    m3u8Seg.tsSize = response.body?.contentLength()?:0L
                    m3u8Seg.mp4StartIndex = startIndex
                    m3u8Seg.mp4EndIndex = endIndex
                    task.m3u8?.tsList?.add(m3u8Seg)
                }
            }
            response.closeQuietly()
            task.m3u8?.success = true
            if (interrupted) {
                if (it.isDisposed) return@create
                it.onComplete()
                return@create
            }
            it.onNext(task.m3u8!!)
        }.timeout(30, TimeUnit.SECONDS)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                if (interrupted) return@subscribe
                // 重新解析
                if (task.m3u8?.errorCode == ERROR_CODE_REQUEST) {
                    doParse(url, callback)
                    return@subscribe
                }
                // 由于有二级请求，需要再次重新解析
                if (task.m3u8?.errorCode == ERROR_CODE_MULTI_STREAMS_REQUEST && task.m3u8?.streams?.isNotEmpty() == true) {
                    Log.d("MyM3U8", "有二级请求列表")
                    // 按带宽或分辨率等信息进行排序
                    task.m3u8?.streams?.sort()
                    // 取视频清晰度最高的流
                    val stream = task.m3u8?.streams?.last()
                    // 重新解析
                    doParse(stream?.url!!, callback)
                    return@subscribe
                }
                // 下载出错
                if (task.m3u8?.success == false) {
                    task.m3u8?.tsList?.clear()
                    task.m3u8?.tsList = null
                    task.m3u8 = null
                    callback.onError(ErrorMessage.ERROR_PARSE_UNKNOWN)
                    return@subscribe
                }
                // 获得最后的解析结果
                if (task.m3u8?.tsList?.isEmpty() == true) {
                    task.m3u8?.tsList?.clear()
                    task.m3u8?.tsList = null
                    task.m3u8 = null
                    callback.onError(ErrorMessage.ERROR_PARSE_UNKNOWN)
                } else {
                    callback.onSuccess(task.m3u8)
                }
            }, {
                if (interrupted) return@subscribe
                it?.printStackTrace()
                val errorMsg = if (it is TimeoutException) {
                    ErrorMessage.ERROR_PARSE_URL_TIME_OUT
                } else {
                    it.cause?.message?:ErrorMessage.ERROR_PARSE_UNKNOWN
                }
                task.m3u8?.tsList?.clear()
                task.m3u8?.tsList = null
                task.m3u8 = null
                callback.onError(errorMsg)
            }, { // 一般都是取消下载
                httpConnectUtils.close()
            })
    }



    /**
     * 取消解析
     */
    fun cancelParse() {
        interrupted = true
        parseDisposable?.dispose()
        parseDisposable = null
    }


    /**
     * 创建2份换成文件
     *  1.m3u解析内容缓存，并追加finalUrl参数
     *  2.m3u8代理缓存，就是1中请求连接全部指向本地地址
     */
    private fun createCacheM3U8Files(): Boolean {
        // 先创建m3u本地缓存解析文件， 将解析内容缓存到本地，以便下次直接使用
        // /Data/Android/xxx/files/Wanban/${fileName}/Proxy/proxy.m3u8
        val proxyM3U8File = FileDownloadPathUtils.getOrCreateProxyM3U8File(task.fileDirPath)
        if (TextUtils.isEmpty(proxyM3U8File)) {
            task.m3u8?.setErrorMessage(ErrorMessage.ERROR_CREATE_PROXY_M3U8_FILE)
            return false
        }
        if (interrupted) return false
        // /Data/Android/xxx/files/Wanban/${fileName}
        val rootDirPath = FileDownloadPathUtils.getOrCreateVideoRootFileDirPath(task.getFileName())
        val proxyM3U8Builder = StringBuilder()
        proxyM3U8Builder.append(Constants.TAG_PLAYLIST_HEADER + "\n")
        proxyM3U8Builder.append(Constants.TAG_VERSION + ":" + task.m3u8?.version + "\n")
        proxyM3U8Builder.append(Constants.TAG_MEDIA_SEQUENCE + ":" + task.m3u8?.sequence + "\n")
        proxyM3U8Builder.append(Constants.TAG_TARGET_DURATION + ":" + task.m3u8?.targetDuration + "\n")
        // ts下载文件目录。/Data/Android/xxx/files/Wanban/${fileName}/TsList/
        val tsFileDirPath = FileDownloadPathUtils.getOrCreateTsFileDirPath(task.fileDirPath)
        for (m3u8Ts in task.m3u8?.tsList!!) {
            if (interrupted) break
            if (m3u8Ts.hasInitSegment) {
                // 当前的ts文件地址，要与之后的下载地址对应上，否则就是无效。/Data/Android/xxx/files/Wanban/${fileName}/TsList/001.ts
                val initSegmentFilePath = FileDownloadPathUtils.getTsFilePath(tsFileDirPath, m3u8Ts.initSegmentName)
                val proxyInitSegmentInfo = if (m3u8Ts.segmentByteRange != null) {
                    "URI=\"$initSegmentFilePath\",BYTERANGE=\"${m3u8Ts.segmentByteRange}"
                } else {
                    "URI=\"$initSegmentFilePath\""
                }
                proxyM3U8Builder.append("${Constants.TAG_INIT_SEGMENT}:$proxyInitSegmentInfo" + "\n")
            }
            if (m3u8Ts.hasKey && m3u8Ts.method != null && m3u8Ts.keyUri != null) {
                // /Data/Android/xxx/files/Wanban/${fileName}/Keys/
                val keyFileDirPath = FileDownloadPathUtils.getM3U8KeyFileDirPath(rootDirPath)
                val keyFile = File(keyFileDirPath, m3u8Ts.localKeyUri)
                var key = "METHOD=" + m3u8Ts.method + ",URI=\"${m3u8Ts.keyUri}\""
                val inputStream = HttpConnectUtils().open(m3u8Ts.keyUri, task.header)?.body?.byteStream()
                if (inputStream != null) {
                    // 创建本地key文件
                    FileUtils.createOrExistsFile(keyFile)
                    // 准备写入秘钥内容
                    val dos = DataOutputStream(FileOutputStream(keyFile))
                    val buffer = ByteArray(4096)
                    var count: Int
                    while (inputStream.read(buffer).also { count = it } > 0) {
                        dos.write(buffer, 0, count)
                    }
                    key += ",IV=" + m3u8Ts.keyIV
                    CloseUtils.closeIOQuietly(dos, inputStream)
                }
                val proxyKey = "METHOD=${m3u8Ts.method}"+ ",URI=\"${keyFile.absolutePath}\",IV=" + m3u8Ts.keyIV
                proxyM3U8Builder.append(Constants.TAG_KEY + ":" + proxyKey + "\n")
            }
            if (interrupted) break
            if (m3u8Ts.hasDiscontinuity) {
                proxyM3U8Builder.append(Constants.TAG_DISCONTINUITY + "\n")
            }
            proxyM3U8Builder.append(Constants.TAG_MEDIA_DURATION + ":" + m3u8Ts.duration + ",\n")
            val tsFilePath = FileDownloadPathUtils.getTsFilePath(tsFileDirPath, m3u8Ts.indexName)
            proxyM3U8Builder.append(tsFilePath + "\n")
        }
        proxyM3U8Builder.append(Constants.TAG_ENDLIST)
        val proxyM3U8 = File(proxyM3U8File!!)
        proxyM3U8.writeText(proxyM3U8Builder.toString())
        return true
    }


    private fun parseM3U8(inputStream: InputStream) {
        try {
            var tsDuration = 0f
            var targetDuration = 0f
            var tsIndex = 0
            var version = 0
            var sequence = 0
            var hasDiscontinuity = false
            var hasKey = false
            var hasInitSegment = false
            var isIFrameOnlyVariant = false
            var method: String? = null
            var encryptionIV: String? = null
            var encryptionKeyUri: String? = null
            var initSegmentUri: String? = null
            var segmentByteRange: String? = null
            val streams = ArrayList<M3U8Stream>()

            if (interrupted) return

            val inputReader = InputStreamReader(inputStream)
            val buffReader = BufferedReader(inputReader)
            var readLine: String? = null
            //分行读取
            while (buffReader.readLine()?.also { readLine = it } != null) {
                if (interrupted) break
                var line = readLine?:break
                line.trim { it <= ' ' }
                if (line.isEmpty()) continue
                if (line.startsWith(Constants.TAG_PREFIX)) { // 标签命令行
                    if (line.startsWith(Constants.TAG_MEDIA_DURATION)) { // 每个片段的时长
                        val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_MEDIA_DURATION)
                        tsDuration = ret?.toFloatOrNull() ?: 0f
                    } else if (line.startsWith(Constants.TAG_I_FRAME_STREAM_INF)) {
                        isIFrameOnlyVariant = true
                    } else if (line.startsWith(Constants.TAG_VERSION)) {
                        val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_VERSION)
                        version = ret?.toIntOrNull() ?: 0
                    } else if (line.startsWith(Constants.TAG_MEDIA_SEQUENCE)) {
                        val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_MEDIA_SEQUENCE)
                        if (!TextUtils.isEmpty(ret)) {
                            sequence = ret?.toIntOrNull() ?: 0
                        }
                    } else if (line.startsWith(Constants.TAG_TARGET_DURATION)) {
                        val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_TARGET_DURATION)
                        if (!TextUtils.isEmpty(ret)) {
                            targetDuration = ret?.toFloatOrNull()?:0f
                        }
                    }else if (line.startsWith(Constants.TAG_STREAM_INF) || isIFrameOnlyVariant) {
                        val stream = M3U8Stream()
                        // val ret = parseMultiStringAttr(line, REGEX_BANDWIDTH)
                        // 获取可变码流中的峰值比特率，单位bits/s
                        if (line.contains(Constants.BANDWIDTH_PRE)) {
                            line = line.replace(Constants.TAG_STREAM_INF_PRE, "")
                            val result = line.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                            for (tag in result) {
                                val target = tag.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                                if (target.size <= 1) continue
                                val extTag = target[0].trim { it <= ' ' }
                                val value = target[1]
                                if (Constants.BANDWIDTH.equals(extTag, ignoreCase = true)) {
                                    stream.peakBitrate = value.toInt()
                                } else if (Constants.AVERAGE_BANDWIDTH.equals(extTag, ignoreCase = true)) {
                                    stream.averageBitrate = value.toInt()
                                } else if (Constants.CODECS.equals(extTag, ignoreCase = true)) {
                                    stream.codecs = value
                                } else if (Constants.RESOLUTION.equals(extTag, ignoreCase = true)) {
                                    val widthAndHeight = value.split("x").toTypedArray()
                                    stream.width = widthAndHeight[0].toIntOrNull() ?: -1
                                    stream.height = widthAndHeight[1].toIntOrNull() ?: -1
                                    if (stream.width <= 0 || stream.height <= 0) {
                                        // Resolution string is invalid.
                                        stream.width = -1
                                        stream.height = -1
                                    }
                                } else if (Constants.FRAME_RATE.equals(extTag, ignoreCase = true)) {
                                    stream.frameRate = value.toFloat()
                                } else if (Constants.VIDEO.equals(extTag, ignoreCase = true)) {
                                    stream.videoGroupId = value
                                } else if (Constants.AUDIO.equals(extTag, ignoreCase = true)) {
                                    stream.audioGroupId = value
                                } else if (isIFrameOnlyVariant && Constants.URI.equals(extTag, ignoreCase = true)) {
                                    stream.url = value
                                }
                            }
                        }
                        streams.add(stream)
                    } else if (line.startsWith(Constants.TAG_DISCONTINUITY)) {
                        hasDiscontinuity = true
                    } else if (line.startsWith(Constants.TAG_ENDLIST)) {
                    } else if (line.startsWith(Constants.TAG_KEY)) {
                        hasKey = true
                        method = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_METHOD)
                        val keyFormat = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_KEYFORMAT)
                        if (Constants.METHOD_NONE != method) {
                            encryptionIV = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_IV)
                            if (Constants.KEYFORMAT_IDENTITY == keyFormat || keyFormat == null) {
                                if (Constants.METHOD_AES_128 == method) {
                                    // The segment is fully encrypted using an identity key.
                                    val tempKeyUri = CommonUtils.parseStringAttr(line, Constants.REGEX_URI)
                                    if (tempKeyUri != null) {
                                        encryptionKeyUri = CommonUtils.getM3U8AbsoluteUrl(task.m3u8?.finalUrl, tempKeyUri)
                                    }
                                } else {
                                    // Do nothing. Samples are encrypted using an identity key,
                                    // but this is not supported. Hopefully, a traditional DRM
                                    // alternative is also provided.
                                }
                            } else {
                                // Do nothing.
                            }
                        }

                    } else if (line.startsWith(Constants.TAG_INIT_SEGMENT)) {
                        val tempInitSegmentUri = CommonUtils.parseStringAttr(line, Constants.REGEX_URI)
                        if (tempInitSegmentUri != null) {
                            hasInitSegment = true
                            initSegmentUri = CommonUtils.getM3U8AbsoluteUrl(task.m3u8?.finalUrl, tempInitSegmentUri)
                            segmentByteRange = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_ATTR_BYTERANGE)
                        }
                    }
                    continue
                }
                if (interrupted) break
                if (streams.isNotEmpty()) {
                    val last = streams[streams.size - 1]
                    if (!isIFrameOnlyVariant) {
                        last.url = CommonUtils.getM3U8AbsoluteUrl(task.m3u8?.finalUrl, line)
                    }
                    continue
                }
                if (interrupted) break
                val ts = M3U8Seg()
                ts.parentUrl = task.m3u8?.finalUrl
                ts.initTsAttributes(CommonUtils.getM3U8AbsoluteUrl(task.m3u8?.finalUrl, line), tsDuration, tsIndex, sequence++, hasDiscontinuity)
                if (hasKey) {
                    ts.setKeyConfig(method, encryptionKeyUri, encryptionIV)
                }
                if (hasInitSegment) {
                    ts.setInitSegmentInfo(initSegmentUri, segmentByteRange)
                }
                task.m3u8?.addTs(ts)
                tsIndex++
                tsDuration = 0f
                hasDiscontinuity = false
                hasKey = false
                hasInitSegment = false
                method = null
                encryptionKeyUri = null
                encryptionIV = null
                initSegmentUri = null
                segmentByteRange = null
            }

            task.m3u8?.streams = streams
            task.m3u8?.targetDuration = targetDuration
            task.m3u8?.version = version
            task.m3u8?.sequence = sequence
            task.m3u8?.success = true
        } catch (e: Exception) {
            e.printStackTrace()
            task.m3u8?.setErrorMessage(e.message)
        } finally {
            inputStream.closeQuietly()
        }
    }



    // 指定文件是否有效
    private fun isFileAvailable(fileDir: String, fileName: String): Boolean{
        val filePath = fileDir + File.separator + fileName
        val file = File(filePath)
        var isFileAvailable = false
        if (file.exists() && file.isFile && file.length() > 100) {
            isFileAvailable = true
        }
        return isFileAvailable
    }

    companion object {

        fun parseM3U8(inputStream: InputStream): M3U8{
            val m3u8 = M3U8()
            try {
                var tsDuration = 0f
                var targetDuration = 0f
                var tsIndex = 0
                var version = 0
                var sequence = 0
                var hasDiscontinuity = false
                var hasEndList = false
                var hasKey = false
                var hasInitSegment = false
                var isIFrameOnlyVariant = false
                var method: String? = null
                var encryptionIV: String? = null
                var encryptionKeyUri: String? = null
                var initSegmentUri: String? = null
                var segmentByteRange: String? = null
                val streams = ArrayList<M3U8Stream>()

                val inputReader = InputStreamReader(inputStream)
                val buffReader = BufferedReader(inputReader)
                var readLine: String? = null
                //分行读取
                while (buffReader.readLine()?.also { readLine = it } != null) {
                    var line = readLine?:break
                    line.trim { it <= ' ' }
                    if (line.isEmpty()) continue
                    if (line.startsWith(Constants.TAG_PREFIX)) { // 标签命令行
                        if (line.startsWith(Constants.TAG_MEDIA_DURATION)) { // 每个片段的时长
                            val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_MEDIA_DURATION)
                            tsDuration = ret?.toFloatOrNull() ?: 0f
                        } else if (line.startsWith(Constants.TAG_I_FRAME_STREAM_INF)) {
                            isIFrameOnlyVariant = true
                        } else if (line.startsWith(Constants.TAG_VERSION)) {
                            val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_VERSION)
                            version = ret?.toIntOrNull() ?: 0
                        } else if (line.startsWith(Constants.TAG_MEDIA_SEQUENCE)) {
                            val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_MEDIA_SEQUENCE)
                            if (!TextUtils.isEmpty(ret)) {
                                sequence = ret?.toIntOrNull() ?: 0
                            }
                        } else if (line.startsWith(Constants.TAG_TARGET_DURATION)) {
                            val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_TARGET_DURATION)
                            if (!TextUtils.isEmpty(ret)) {
                                targetDuration = ret?.toFloatOrNull()?:0f
                            }
                        }else if (line.startsWith(Constants.TAG_STREAM_INF) || isIFrameOnlyVariant) {
                            val stream = M3U8Stream()
                            // val ret = parseMultiStringAttr(line, REGEX_BANDWIDTH)
                            // 获取可变码流中的峰值比特率，单位bits/s
                            if (line.contains(Constants.BANDWIDTH_PRE)) {
                                line = line.replace(Constants.TAG_STREAM_INF_PRE, "")
                                val result = line.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                                for (tag in result) {
                                    val target = tag.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                                    if (target.size <= 1) continue
                                    val extTag = target[0].trim { it <= ' ' }
                                    val value = target[1]
                                    if (Constants.BANDWIDTH.equals(extTag, ignoreCase = true)) {
                                        stream.peakBitrate = value.toInt()
                                    } else if (Constants.AVERAGE_BANDWIDTH.equals(extTag, ignoreCase = true)) {
                                        stream.averageBitrate = value.toInt()
                                    } else if (Constants.CODECS.equals(extTag, ignoreCase = true)) {
                                        stream.codecs = value
                                    } else if (Constants.RESOLUTION.equals(extTag, ignoreCase = true)) {
                                        val widthAndHeight = value.split("x").toTypedArray()
                                        stream.width = widthAndHeight[0].toIntOrNull() ?: -1
                                        stream.height = widthAndHeight[1].toIntOrNull() ?: -1
                                        if (stream.width <= 0 || stream.height <= 0) {
                                            // Resolution string is invalid.
                                            stream.width = -1
                                            stream.height = -1
                                        }
                                    } else if (Constants.FRAME_RATE.equals(extTag, ignoreCase = true)) {
                                        stream.frameRate = value.toFloat()
                                    } else if (Constants.VIDEO.equals(extTag, ignoreCase = true)) {
                                        stream.videoGroupId = value
                                    } else if (Constants.AUDIO.equals(extTag, ignoreCase = true)) {
                                        stream.audioGroupId = value
                                    } else if (isIFrameOnlyVariant && Constants.URI.equals(extTag, ignoreCase = true)) {
                                        stream.url = value
                                    }
                                }
                            }
                            streams.add(stream)
                        } else if (line.startsWith(Constants.TAG_DISCONTINUITY)) {
                            hasDiscontinuity = true
                        } else if (line.startsWith(Constants.TAG_ENDLIST)) {
                            hasEndList = true
                        } else if (line.startsWith(Constants.TAG_KEY)) {
                            hasKey = true
                            method = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_METHOD)
                            val keyFormat = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_KEYFORMAT)
                            if (Constants.METHOD_NONE != method) {
                                encryptionIV = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_IV)
                                if (Constants.KEYFORMAT_IDENTITY == keyFormat || keyFormat == null) {
                                    if (Constants.METHOD_AES_128 == method) {
                                        // The segment is fully encrypted using an identity key.
                                        val tempKeyUri = CommonUtils.parseStringAttr(line, Constants.REGEX_URI)
                                        if (tempKeyUri != null) {
                                            encryptionKeyUri = CommonUtils.getM3U8AbsoluteUrl(m3u8.finalUrl, tempKeyUri)
                                        }
                                    } else {
                                        // Do nothing. Samples are encrypted using an identity key,
                                        // but this is not supported. Hopefully, a traditional DRM
                                        // alternative is also provided.
                                    }
                                } else {
                                    // Do nothing.
                                }
                            }

                        } else if (line.startsWith(Constants.TAG_INIT_SEGMENT)) {
                            val tempInitSegmentUri = CommonUtils.parseStringAttr(line, Constants.REGEX_URI)
                            if (tempInitSegmentUri != null) {
                                hasInitSegment = true
                                initSegmentUri = CommonUtils.getM3U8AbsoluteUrl(m3u8.finalUrl, tempInitSegmentUri)
                                segmentByteRange = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_ATTR_BYTERANGE)
                            }
                        }
                        continue
                    }
                    if (streams.isNotEmpty()) {
                        val last = streams[streams.size - 1]
                        if (!isIFrameOnlyVariant) {
                            last.url = CommonUtils.getM3U8AbsoluteUrl(m3u8.finalUrl, line)
                        }
                        continue
                    }
                    val ts = M3U8Seg()
                    ts.parentUrl = m3u8.finalUrl
                    ts.initTsAttributes(CommonUtils.getM3U8AbsoluteUrl(m3u8.finalUrl, line), tsDuration, tsIndex, sequence++, hasDiscontinuity)
                    if (hasKey) {
                        ts.setKeyConfig(method, encryptionKeyUri, encryptionIV)
                    }
                    if (hasInitSegment) {
                        ts.setInitSegmentInfo(initSegmentUri, segmentByteRange)
                    }
                    m3u8.addTs(ts)
                    tsIndex++
                    tsDuration = 0f
                    hasDiscontinuity = false
                    hasKey = false
                    hasInitSegment = false
                    method = null
                    encryptionKeyUri = null
                    encryptionIV = null
                    initSegmentUri = null
                    segmentByteRange = null
                }

                m3u8.streams = streams
                m3u8.targetDuration = targetDuration
                m3u8.version = version
                m3u8.sequence = sequence
                m3u8.success = true
                return m3u8
            } catch (e: Exception) {
                e.printStackTrace()
                m3u8.setErrorMessage(e.message)
                return m3u8
            } finally {
                inputStream.closeQuietly()

            }
        }



    }


}


//private fun parseM3U8(m3uLines: List<String>) {
//    try {
//        var tsDuration = 0f
//        var targetDuration = 0f
//        var tsIndex = 0
//        var version = 0
//        var sequence = 0
//        var hasDiscontinuity = false
//        var hasEndList = false
//        var hasKey = false
//        var hasInitSegment = false
//        var isIFrameOnlyVariant = false
//        var method: String? = null
//        var encryptionIV: String? = null
//        var encryptionKeyUri: String? = null
//        var initSegmentUri: String? = null
//        var segmentByteRange: String? = null
//        val streams = ArrayList<M3U8Stream>()
//
//        for (readLine in  m3uLines) {
//            if (interrupted) break
//            readLine.trim { it <= ' ' }
//            if (readLine.isEmpty()) continue
//            var line = readLine
//            if (line.startsWith(Constants.TAG_PREFIX)) { // 标签命令行
//                if (line.startsWith(Constants.TAG_MEDIA_DURATION)) { // 每个片段的时长
//                    val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_MEDIA_DURATION)
//                    tsDuration = ret?.toFloatOrNull() ?: 0f
//                } else if (line.startsWith(Constants.TAG_I_FRAME_STREAM_INF)) {
//                    isIFrameOnlyVariant = true
//                } else if (line.startsWith(Constants.TAG_VERSION)) {
//                    val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_VERSION)
//                    version = ret?.toIntOrNull() ?: 0
//                } else if (line.startsWith(Constants.TAG_MEDIA_SEQUENCE)) {
//                    val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_MEDIA_SEQUENCE)
//                    if (!TextUtils.isEmpty(ret)) {
//                        sequence = ret?.toIntOrNull() ?: 0
//                    }
//                } else if (line.startsWith(Constants.TAG_TARGET_DURATION)) {
//                    val ret = CommonUtils.parseStringAttr(line, Constants.REGEX_TARGET_DURATION)
//                    if (!TextUtils.isEmpty(ret)) {
//                        targetDuration = ret?.toFloatOrNull()?:0f
//                    }
//                }else if (line.startsWith(Constants.TAG_STREAM_INF) || isIFrameOnlyVariant) {
//                    val stream = M3U8Stream()
//                    // val ret = parseMultiStringAttr(line, REGEX_BANDWIDTH)
//                    // 获取可变码流中的峰值比特率，单位bits/s
//                    if (line.contains(Constants.BANDWIDTH_PRE)) {
//                        line = line.replace(Constants.TAG_STREAM_INF_PRE, "")
//                        val result = line.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
//                        for (tag in result) {
//                            val target = tag.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
//                            if (target.size <= 1) continue
//                            val extTag = target[0].trim { it <= ' ' }
//                            val value = target[1]
//                            if (Constants.BANDWIDTH.equals(extTag, ignoreCase = true)) {
//                                stream.peakBitrate = value.toInt()
//                            } else if (Constants.AVERAGE_BANDWIDTH.equals(extTag, ignoreCase = true)) {
//                                stream.averageBitrate = value.toInt()
//                            } else if (Constants.CODECS.equals(extTag, ignoreCase = true)) {
//                                stream.codecs = value
//                            } else if (Constants.RESOLUTION.equals(extTag, ignoreCase = true)) {
//                                val widthAndHeight = value.split("x").toTypedArray()
//                                stream.width = widthAndHeight[0].toIntOrNull() ?: -1
//                                stream.height = widthAndHeight[1].toIntOrNull() ?: -1
//                                if (stream.width <= 0 || stream.height <= 0) {
//                                    // Resolution string is invalid.
//                                    stream.width = -1
//                                    stream.height = -1
//                                }
//                            } else if (Constants.FRAME_RATE.equals(extTag, ignoreCase = true)) {
//                                stream.frameRate = value.toFloat()
//                            } else if (Constants.VIDEO.equals(extTag, ignoreCase = true)) {
//                                stream.videoGroupId = value
//                            } else if (Constants.AUDIO.equals(extTag, ignoreCase = true)) {
//                                stream.audioGroupId = value
//                            } else if (isIFrameOnlyVariant && Constants.URI.equals(extTag, ignoreCase = true)) {
//                                stream.url = value
//                            }
//                        }
//                    }
//                    streams.add(stream)
//                } else if (line.startsWith(Constants.TAG_DISCONTINUITY)) {
//                    hasDiscontinuity = true
//                } else if (line.startsWith(Constants.TAG_ENDLIST)) {
//                    hasEndList = true
//                } else if (line.startsWith(Constants.TAG_KEY)) {
//                    hasKey = true
//                    method = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_METHOD)
//                    val keyFormat = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_KEYFORMAT)
//                    if (Constants.METHOD_NONE != method) {
//                        encryptionIV = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_IV)
//                        if (Constants.KEYFORMAT_IDENTITY == keyFormat || keyFormat == null) {
//                            if (Constants.METHOD_AES_128 == method) {
//                                // The segment is fully encrypted using an identity key.
//                                val tempKeyUri = CommonUtils.parseStringAttr(line, Constants.REGEX_URI)
//                                if (tempKeyUri != null) {
//                                    encryptionKeyUri = CommonUtils.getM3U8AbsoluteUrl(task.m3u8?.finalUrl, tempKeyUri)
//                                }
//                            } else {
//                                // Do nothing. Samples are encrypted using an identity key,
//                                // but this is not supported. Hopefully, a traditional DRM
//                                // alternative is also provided.
//                            }
//                        } else {
//                            // Do nothing.
//                        }
//                    }
//
//                } else if (line.startsWith(Constants.TAG_INIT_SEGMENT)) {
//                    val tempInitSegmentUri = CommonUtils.parseStringAttr(line, Constants.REGEX_URI)
//                    if (tempInitSegmentUri != null) {
//                        hasInitSegment = true
//                        initSegmentUri = CommonUtils.getM3U8AbsoluteUrl(task.m3u8?.finalUrl, tempInitSegmentUri)
//                        segmentByteRange = CommonUtils.parseOptionalStringAttr(line, Constants.REGEX_ATTR_BYTERANGE)
//                    }
//                }
//                continue
//            }
//            if (interrupted) break
//            if (streams.isNotEmpty()) {
//                val last = streams[streams.size - 1]
//                if (!isIFrameOnlyVariant) {
//                    last.url = CommonUtils.getM3U8AbsoluteUrl(task.m3u8?.finalUrl, line)
//                }
//                continue
//            }
//            if (interrupted) break
//            val ts = M3U8Seg()
//            ts.parentUrl = task.m3u8?.finalUrl
//            ts.initTsAttributes(CommonUtils.getM3U8AbsoluteUrl(task.m3u8?.finalUrl, line), tsDuration, tsIndex, sequence++, hasDiscontinuity)
//            if (hasKey) {
//                ts.setKeyConfig(method, encryptionKeyUri, encryptionIV)
//            }
//            if (hasInitSegment) {
//                ts.setInitSegmentInfo(initSegmentUri, segmentByteRange)
//            }
//            task.m3u8?.addTs(ts)
//            tsIndex++
//            tsDuration = 0f
//            hasDiscontinuity = false
//            hasKey = false
//            hasInitSegment = false
//            method = null
//            encryptionKeyUri = null
//            encryptionIV = null
//            initSegmentUri = null
//            segmentByteRange = null
//        }
//        task.m3u8?.streams = streams
//        task.m3u8?.targetDuration = targetDuration
//        task.m3u8?.version = version
//        task.m3u8?.sequence = sequence
//        task.m3u8?.hasEndList = hasEndList
//        task.m3u8?.success = true
//    } catch (e: Exception) {
//        e.printStackTrace()
//        task.m3u8?.setErrorMessage(e.message)
//    }
//}




///**
// * 解析本地M3U
// */
//private fun parseM3U8Local(m3u8: M3U8, localM3U8File: File): M3U8 {
//    // 读取M3U文件
//    try {
//        if (interrupted) return m3u8
//        val localM3U = FileIOUtils.readFile2String(localM3U8File)
//        if (TextUtils.isEmpty(localM3U)) { // 如果没读取成功，删除文件，重新解析
//            FileUtils.delete(localM3U8File)
//            m3u8.errorCode = ERROR_CODE_REQUEST
//            return m3u8
//        }
//        if (interrupted) return m3u8
//        // 按行读取内容
//        val targetList = ArrayList<String>(localM3U.lines())
//        // 最后一行默认是finalUrl
//        val finalUrlLocal = targetList.removeLast()
//        if (!finalUrlLocal.startsWith("final")) {// 如果找不到finalUrl，删除文件，重新解析
//            FileUtils.delete(localM3U8File)
//            m3u8.errorCode = ERROR_CODE_REQUEST
//            return m3u8
//        }
//        if (interrupted) return m3u8
//        val result = finalUrlLocal.split("=")
//        if (result.size < 2) { // 如果找到finalUrl，删除文件，重新解析
//            FileUtils.delete(localM3U8File)
//            if (interrupted) return m3u8
//            m3u8.errorCode = ERROR_CODE_REQUEST
//            return m3u8
//        }
//        m3u8.finalUrl = result[1]
//        m3u8.success = true
//        // 找到配置文件，直接解析
//        return parseM3U8(m3u8, targetList)
//    } catch (e: java.lang.Exception) {
//        e.printStackTrace()
//        m3u8.errorCode = ERROR_CODE_REQUEST
//        return m3u8
//    }
//}
//
//



///**
// * 将解析获取到的M3U8，按照指定的格式，写入本地缓存文件
// * 并追加finalUrl参数
// */
//fun createLocalM3U8(m3u8: M3U8): Boolean {
//    // 先创建m3u本地缓存解析文件， 将解析内容缓存到本地，以便下次直接使用
//    // /Data/Android/xxx/files/Wanban/${fileName}/M3U/local.m3u
//    val m3uFilePath = FileDownloadPathUtils.geM3UFilePath(task.fileDirPath)
//    if (TextUtils.isEmpty(m3uFilePath)) {
//        m3u8.setErrorMessage(ErrorMessage.ERROR_CREATE_LOCAL_M3U8_FILE)
//        return false
//    }
//    if (interrupted) return false
//    // /Data/Android/xxx/files/Wanban/${fileName}
//    val rootDirPath = FileDownloadPathUtils.getOrCreateVideoRootFileDirPath(m3u8.videoName)
//    var bfw: BufferedWriter? = null
//    try {
//        bfw = BufferedWriter(FileWriter(m3uFilePath, false))
//        bfw.write(Constants.TAG_PLAYLIST_HEADER + "\n")
//        bfw.write((Constants.TAG_VERSION + ":" + m3u8.version) + "\n")
//        bfw.write(Constants.TAG_MEDIA_SEQUENCE + ":" + m3u8.sequence + "\n")
//        bfw.write(Constants.TAG_TARGET_DURATION + ":" + m3u8.targetDuration + "\n")
//        for (m3u8Ts in m3u8.tsList) {
//            if (interrupted) break
//            if (m3u8Ts.hasInitSegment) {
//                val initSegmentInfo = if (m3u8Ts.segmentByteRange != null) {
//                    "URI=\"" + m3u8Ts.initSegmentUri + "\"" + ",BYTERANGE=\"" + m3u8Ts.segmentByteRange + "\""
//                } else {
//                    "URI=\"" + m3u8Ts.initSegmentUri + "\""
//                }
//                bfw.write(Constants.TAG_INIT_SEGMENT + ":" + initSegmentInfo + "\n")
//            }
//            if (interrupted) break
//            if (m3u8Ts.hasKey) {
//                if (m3u8Ts.method != null) {
//                    var key = "METHOD=" + m3u8Ts.method
//                    if (m3u8Ts.keyUri != null) {
//                        val keyUri = m3u8Ts.keyUri
//                        key += ",URI=\"$keyUri\""
//                        val httpConnectUtils = HttpConnectUtils()
//                        val inputStream = httpConnectUtils.open(keyUri, task.header)?.body?.byteStream()
//                        if (inputStream != null) {
//                            val dis = DataInputStream(inputStream)
//                            // /Data/Android/xxx/files/Wanban/${fileName}/Keys/
//                            val keyFileDirPath = FileDownloadPathUtils.getM3U8KeyFileDirPath(rootDirPath)
//                            val keyFile = File(keyFileDirPath, m3u8Ts.localKeyUri)
//                            // 创建key文件
//                            FileUtils.createOrExistsFile(keyFile)
//                            // 准备写入秘钥内容
//                            val dos = DataOutputStream(FileOutputStream(keyFile))
//                            val buffer = ByteArray(4096)
//                            var count: Int
//                            while (dis.read(buffer).also { count = it } > 0) {
//                                dos.write(buffer, 0, count)
//                            }
//                            if (m3u8Ts.keyIV != null) {
//                                key += ",IV=" + m3u8Ts.keyIV
//                            }
//                            CloseUtils.closeIOQuietly(dis, dos, inputStream)
//                        }
//                    }
//                    bfw.write(Constants.TAG_KEY + ":" + key + "\n")
//                }
//            }
//            if (interrupted) break
//            if (m3u8Ts.hasDiscontinuity) {
//                bfw.write(Constants.TAG_DISCONTINUITY + "\n")
//            }
//            bfw.write(Constants.TAG_MEDIA_DURATION + ":" + m3u8Ts.duration + ",\n")
//            bfw.write(m3u8Ts.url)
//            if (interrupted) break
//            bfw.newLine()
//        }
//        bfw.write(Constants.TAG_ENDLIST)
//        bfw.newLine()
//        bfw.write("finalUrl=" + m3u8.finalUrl)
//        bfw.flush()
//        m3u8.success = true
//        return true
//    } catch (e: Exception) {
//        e.printStackTrace()
//        m3u8.setErrorMessage(ErrorMessage.ERROR_WRITE_LOCAL_M3U8_FILE)
//        return false
//    } finally {
//        CloseUtils.closeIOQuietly(bfw)
//    }
//}
//
///**
// * 创建本地代理的M3U8索引文件 （所有的请求都应该指向本地）
// * @param m3u8
// * @throws Exception
// */
//fun createProxyM3U8File(m3u8: M3U8): Boolean {
//    var bfw: BufferedWriter? = null
//    try {
//        // /Data/Android/xxx/files/Wanban/${fileName}/Proxy/proxy.m3u8
//        val proxyM3U8File = FileDownloadPathUtils.getOrCreateProxyM3U8File(task.fileDirPath)
//        if (TextUtils.isEmpty(proxyM3U8File)) {
//            m3u8.setErrorMessage(ErrorMessage.ERROR_CREATE_PROXY_M3U8_FILE)
//            return false
//        }
//        if (interrupted) return false
//        bfw = BufferedWriter(FileWriter(proxyM3U8File, false))
//        bfw.write(Constants.TAG_PLAYLIST_HEADER + "\n")
//        bfw.write("${Constants.TAG_VERSION}:${m3u8.version}" + "\n")
//        bfw.write("${Constants.TAG_MEDIA_SEQUENCE}:${m3u8.sequence}" + "\n")
//        bfw.write("${Constants.TAG_TARGET_DURATION}:${m3u8.targetDuration}" + "\n")
//        // ts下载文件目录。/Data/Android/xxx/files/Wanban/${fileName}/TsList/
//        val tsFileDirPath = FileDownloadPathUtils.getOrCreateTsFileDirPath(task.fileDirPath)
//        for (m3u8Ts in m3u8.tsList) {
//            if (interrupted) break
//            // 当前的ts文件地址，要与之后的下载地址对应上，否则就是无效。/Data/Android/xxx/files/Wanban/${fileName}/TsList/001.ts
//            val initSegmentFilePath = FileDownloadPathUtils.getTsFilePath(tsFileDirPath, m3u8Ts.initSegmentName)
//            if (m3u8Ts.hasInitSegment) {
//                val initSegmentInfo = if (m3u8Ts.segmentByteRange != null) {
//                    "URI=\"$initSegmentFilePath\",BYTERANGE=\"${m3u8Ts.segmentByteRange}"
//                } else {
//                    "URI=\"$initSegmentFilePath\""
//                }
//                bfw.write("${Constants.TAG_INIT_SEGMENT}:$initSegmentInfo" + "\n")
//            }
//            if (interrupted) break
//            if (m3u8Ts.hasKey && !TextUtils.isEmpty(m3u8Ts.method)) {
//                var key = "METHOD=${m3u8Ts.method}"
//                if (!TextUtils.isEmpty(m3u8Ts.keyUri)) {
//                    // /Data/Android/xxx/files/Wanban/${fileName}/Keys/${m3u8Ts.localKeyUri}
//                    val keyFile = FileDownloadPathUtils.getM3U8KeyFileDirPath(task.fileDirPath) + File.separator + m3u8Ts.localKeyUri
//                    key += ",URI=\"$keyFile\""
//                    if (m3u8Ts.keyIV != null) {
//                        key += ",IV=" + m3u8Ts.keyIV
//                    }
//                }
//                bfw.write("${Constants.TAG_KEY}:$key" + "\n")
//            }
//            if (interrupted) break
//            if (m3u8Ts.hasDiscontinuity) {
//                bfw.write(Constants.TAG_DISCONTINUITY + "\n")
//            }
//            bfw.write("${Constants.TAG_MEDIA_DURATION}:${m3u8Ts.duration},\n")
//            val tsFilePath = FileDownloadPathUtils.getTsFilePath(tsFileDirPath, m3u8Ts.indexName)
//            bfw.write(tsFilePath)
//            bfw.newLine()
//        }
//        bfw.write(Constants.TAG_ENDLIST)
//        bfw.flush()
//        m3u8.success = true
//        Log.d("M3U8ParseUtils", "代理解析文件下载完成...")
//        return true
//    } catch (e: java.lang.Exception) {
//        e.printStackTrace()
//        m3u8.setErrorMessage(ErrorMessage.ERROR_WRITE_PROXY_M3U8_FILE)
//        return false
//    } finally {
//        CloseUtils.closeIO(bfw)
//    }
//}
